// * instanceof 操作符用于检查一个对象是否属于某个特定的 class（类）。同时，它还考虑了继承。

// * instanceof 操作符。如果 obj 隶属于 Class 类（或 Class 类的衍生类），则返回 true。

// * instanceof 在检查中会将原型链考虑在内。所以下面的 arr 对象也隶属于 Object 类。因为 Array 继承自 Object。

// ****************************************************************************************************

// * 例子1：class 类
class Rabbit {}

let rabbit = new Rabbit()

// rabbit 是 Rabbit 类的实例吗？
console.log(rabbit instanceof Rabbit) // true

// * 例子2：构造函数
function Rabbit2() {}

console.log(new Rabbit2() instanceof Rabbit2) // true

// * 例子3：内建类
let arr = [1, 2, 3]

console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true

// ****************************************************************************************************

// * obj instanceof Class 算法执行过程大致如下：

// * 1. 如果这儿有静态方法 Symbol.hasInstance 那就直接调用这个方法：
// 例如：
// 设置 instanceof 检查
// 并假设具有 canEat 属性的都是 animal
class Animal {
	static [Symbol.hasInstance](obj) {
		if (obj.canEat) return true
	}
}

let aObj = { canEat: true }

// 校验时 Animal[Symbol.hasInstance](obj) 被调用
console.log(aObj instanceof Animal) // true

// * 2. 大多数 class 没有 Symbol.hasInstance 静态方法。在这种情况下，标准的逻辑是：obj instanceof Class 检查 Class.prototype 是否等于 obj 的原型链中的原型之一。
// 例如：
// * obj.__proto__ === Class.prototype ?
// * obj.__proto__.__proto__ === Class.prototype ?
// * obj.__proto__.__proto__.__proto__ === Class.prototype ?
// * ......
// * 如果任意一个的答案为 true，则返回 true；否者，如果已经检查到原型链的尾端了，返回 false。

// 代码示例：
class A {}
class B extends A {}

const b = new B()

console.log(b instanceof B) // true (instanceof 判断方式：b.__proto__ === B.prototype)
console.log(b instanceof A) // true (instanceof 判断方式：b.__proto__.__proto__ === A.prototype)

// ****************************************************************************************************

// * 方法：objA.isPrototypeOf(objB) 判断 objA 处在 ObjB 的原型链中，则返回 true。
// * 所以，可以将 obj instanceof Class 检查改为 Class.prototype.isPrototypeOf(obj)。

class C {}
const c = new C()
console.log(C.prototype.isPrototypeOf(c)) // true

// ****** 福利：使用 Object.prototype.toString 方法来揭示类型 ******

// 将 toString 方法复制到一个变量中
const s = Object.prototype.toString

console.log(s.call({})) // [object Object]
console.log(s.call([])) // [object Array]
console.log(s.call(function () {})) // [object Function]

console.log(s.call('str')) // [object String]
console.log(s.call(1)) // [object Number]
console.log(s.call(null)) // [object Null]
console.log(s.call(undefined)) // [object Undefined]
console.log(s.call(false)) // [object Boolean]

console.log(s.call(Symbol('id'))) // [object Symbol]
console.log(s.call(123n)) // [object BigInt]

// ****************************************************************************************************

// * 使用 Symbol.toStringTag 方法，自定义对象的 toString 方法的行为。

// * 例子：自定义对象的 toString 方法
let user = {
	[Symbol.toStringTag]: 'User',
}

console.log({}.toString.call(user)) // [object User]

// * 从技术上讲，{}.toString 是一种“更高级的” typeof。

// * 于大多数特定于环境的对象，都有一个这样的属性。包含 Symbol.toStringTag 属性的内建对象：
// console.log(window[Symbol.toStringTag]) // Window
// console.log(XMLHttpRequest.prototype[Symbol.toStringTag]) // XMLHttpRequest

// * 检查内建对象：
// console.log({}.toString.call(window)) // [object Window]
// console.log({}.toString.call(new XMLHttpRequest())) // [object XMLHttpRequest]
